package licode.unisop.authcenter.application.executor;

import cn.hutool.core.util.StrUtil;
import licode.unisop.authcenter.application.info.*;
import licode.unisop.authcenter.exception.CenterErrorCode;
import licode.unisop.client.info.SopTokenInfo;
import licode.unisop.platform.client.api.SiPlatformAuthService;
import licode.unisop.platform.client.api.SiPlatformQueryService;
import licode.unisop.provider.conf.SiAssetState;
import licode.unisop.provider.info.*;
import licode.unisop.provider.utils.SiAssetUtil;
import licode.unisop.provider.vo.*;
import licode.unisop.realm.client.api.SiRealmQueryService;
import licode.unisop.realm.client.api.SiUserQueryService;
import licode.unisop.realm.client.dto.SiRealmDTO;
import licode.unisop.types.AuthConst;
import lombok.Getter;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 定义统一认证平台中心资源查询相关接口
 *
 * @author licode
 */
@Service
public class CenterAssetQueryExecutor {
    @Resource
    private SiRealmQueryService siRealmQueryService;

    @Resource
    private SiUserQueryService siUserQueryService;

    @Getter
    @Resource
    private SiPlatformAuthService siPlatformAuthService;

    @Resource
    private SiPlatformQueryService siPlatformQueryService;

    @Resource
    private CenterPlatformQueryExecutor centerPlatformQueryExecutor;

    public SiPageResult<SiAssetBrief> loadAssetByPage(SopTokenInfo tokenInfo, PlatAssetQueryPageIn req) {
        SiPageResult<SiAssetBrief> result;

        result = siPlatformQueryService.findAssetPage(AssetPageInfo.builder()
                .current(req.getCurrent())
                .pageSize(req.getPageSize())
                .platformId(req.getTargetId())
                .assetType(req.getAssetType())
                .parentId(req.getParentId())
                .name(req.getName())
                .state(req.getState())
                .build());

        return result;
    }

    public SiPageResult<SiAssetBrief> queryMovePage(PlatMoveAssetQueryPageIn req) {
        return queryPageByLevel(PlatAssetByLevelPageIn.builder()
                .current(req.getCurrent())
                .pageSize(req.getPageSize())
                .server(req.getServer())
                .targetId(req.getTargetId())
                .parentId(null == req.getParentId() ? "" : req.getParentId())
                .name(req.getName())
                .assetType(req.getAssetType())
                .filterAssets(Collections.singletonList(req.getAssetId()))
                .build());
    }

    /**
     * 按层级获取指定类型的资源
     */
    public List<SiAsset> queryAllByLevel(PlatAllAssetByLevelIn req) {
        List<SiAsset> result = new ArrayList<>();
        if ("ASSET".equals(req.getServer())) {
            SiAssetInfo assetInfo;
            assetInfo = siPlatformQueryService.getPlatformAssets(SiPlatIdFind.builder()
                    .platformId(req.getTargetId()).build());
            if (null != assetInfo && null != assetInfo.getAssets()) {
                assetInfo.getTrees().forEach(item -> {
                    if (req.getAssetType().equals(item.getType())) {
                        result.add(assetInfo.getAssets().get(item.getId()));
                    }
                });

                SiAssetUtil.sortAsset(result);
            }
        }

        return result;
    }

    /**
     * 按层级分页查询资源信息，必须传入父资源ID，如果父资源ID为空，则查询顶层所有资源
     */
    public SiPageResult<SiAssetBrief> queryPageByLevel(PlatAssetByLevelPageIn req) {
        if ("REALM".equals(req.getServer())) {
            return queryRealmPageByLevel(req);
        } else if ("USER".equals(req.getAssetType())) {
            return queryRealmUserPage(req);
        } else {
            return siPlatformQueryService.findAssetPage(AssetPageInfo.builder()
                    .current(req.getCurrent())
                    .pageSize(req.getPageSize())
                    .parentId(req.getParentId())
                    .assetType(req.getAssetType())
                    .platformId(req.getTargetId())
                    .name(req.getName())
                    .state(req.getState())
                    .filterAssets(req.getFilterAssets())
                    .filterRelMode(req.getFilterRelMode())
                    .filterRelIds(req.getFilterRelIds())
                    .filterRelType(req.getFilterRelType())
                    .build());
        }
    }

    private List<String> queryRelTargetIds(PlatAssetByLevelPageIn req, List<SiUser> data) {
        SiPageResult<SiAssetRelation> relPage;
        relPage = siPlatformQueryService.findHoldRelAssetPage(HoldAssetPageInfo.builder()
                .ownerId(req.getTargetId())
                .holdId(req.getFilterRelIds().get(0))
                .holdType(req.getFilterRelType())
                .assetType(req.getAssetType())
                .assetIds(data.stream().map(SiUser::getId)
                        .collect(Collectors.toList()))
                .current(1)
                .pageSize(data.size())
                .build());
        if (null == relPage || relPage.getData() == null || relPage.getData().isEmpty()) {
            return new ArrayList<>();
        } else {
            return relPage.getData().stream().map(SiAssetRelation::getTargetId)
                    .collect(Collectors.toList());
        }
    }

    private List<String> queryRelHoldIds(PlatAssetByLevelPageIn req, List<SiUser> data) {
        SiPageResult<SiAssetRelation> relPage;
        relPage = siPlatformQueryService.findAssetRelHoldPage(AssetHoldPageInfo.builder()
                .ownerId(req.getTargetId())
                .holdType(req.getAssetType())
                .holdIds(data.stream().map(SiUser::getId)
                        .collect(Collectors.toList()))
                .assetType(req.getFilterRelType())
                .assetId(req.getFilterRelIds().get(0))
                .current(1)
                .pageSize(data.size())
                .build());
        if (null == relPage || relPage.getData() == null || relPage.getData().isEmpty()) {
            return new ArrayList<>();
        } else {
            return relPage.getData().stream().map(SiAssetRelation::getAssetId)
                    .collect(Collectors.toList());
        }
    }

    public SiPageResult<SiAssetBrief> queryRealmUserPage(PlatAssetByLevelPageIn req) {
        SiPageResult<SiUser> userPage;
        userPage = centerPlatformQueryExecutor.queryUserPage(PlatUserQueryPageIn.builder()
                .current(req.getCurrent())
                .pageSize(req.getPageSize())
                .platformId(req.getTargetId())
                .name(req.getName())
                .state(SiAssetState.AT_ACTIVATE)
                .build());

        if (null != userPage && userPage.getData() != null && !userPage.getData().isEmpty()) {
            List<String> targetIds;
            if (Integer.valueOf(1).equals(req.getFilterRelMode())) {
                targetIds = queryRelTargetIds(req, userPage.getData());
            } else {
                targetIds = queryRelHoldIds(req, userPage.getData());
            }

            SiPageResult<SiAssetBrief> result = new SiPageResult<>();
            result.setCurrent(userPage.getCurrent());
            result.setPageSize(userPage.getPageSize());
            result.setCount(userPage.getCount());
            result.setTotal(userPage.getTotal());
            result.setData(userPage.getData().stream().map(item -> {
                SiProps props = new SiProps();
                if (targetIds.contains(item.getId())) {
                    props.addProp("exist", 1);
                } else {
                    props.addProp("exist", 0);
                }
                return SiAssetBrief.builder()
                        .id(item.getId())
                        .name(StrUtil.isBlank(item.getNickName()) ? item.getName() : item.getNickName())
                        .state(item.getState())
                        .code(item.getPhone())
                        .parentId("")
                        .type("USER")
                        .props(props)
                        .build();
            }).collect(Collectors.toList()));
            return result;
        } else {
            return null;
        }
    }

    public SiPageResult<SiAssetBrief> queryRealmPageByLevel(PlatAssetByLevelPageIn req) {
        SiPageResult<SiUserGroup> result;
        SiPageResult<SiAssetBrief> assetRet;

        if (null == req.getParentId()) {
            throw CenterErrorCode.PARAMS_IS_DONT_EXIST.buildException();
        }
        result = siUserQueryService.findUserGroupPage(UserGroupQueryPageIn.builder()
                .current(req.getCurrent())
                .pageSize(req.getPageSize())
                .realmId(req.getTargetId())
                .name(req.getName())
                .state(req.getState())
                .build());
        if (null == result) {
            return null;
        } else {
            assetRet = new SiPageResult<>();
            assetRet.setCount(result.getCount());
            assetRet.setCurrent(result.getCurrent());
            assetRet.setPageSize(result.getPageSize());
            assetRet.setTotal(result.getTotal());

            if (null != result.getData()) {
                assetRet.setData(result.getData().stream()
                        .map(item -> SiAssetBrief.builder()
                                .id(item.getId())
                                .name(item.getName())
                                .state(item.getState())
                                .level(item.getLevel())
                                .rootId(item.getId())
                                .parentId("")
                                .type("USER_GROUP")
                                .build())
                        .collect(Collectors.toList()));
            } else {
                assetRet.setData(new ArrayList<>());
            }
            return assetRet;
        }
    }

    /**
     * 分页查询指定资源拥有的所有资源信息，比如查询指定分组下所有的权限信息
     */
    public SiPageResult<SiAssetBrief> queryAssetsOfHolderPage(AssetsOfHolderPageIn req) {
        if (!AuthConst.REALM_SERVER.equals(req.getServer())) {
            if ("USER".equals(req.getAssetType())) {
                return queryUsersOfGroupPage(req);
            } else {
                return siPlatformQueryService.findHoldAssetPage(HoldAssetPageInfo.builder()
                        .ownerId(req.getTargetId())
                        .current(req.getCurrent())
                        .pageSize(req.getPageSize())
                        .holdId(req.getHoldId())
                        .holdType(req.getHoldType())
                        .assetType(req.getAssetType())
                        .build());
            }
        }
        return null;
    }

    public SiPageResult<SiAssetBrief> queryUsersOfGroupPage(AssetsOfHolderPageIn req) {
        SiPageResult<SiAssetRelation> result;
        SiPlatform platform = siPlatformQueryService.lookupPlatform(SiPlatIdFind.builder()
                .platformId(req.getTargetId()).build());

        if (null == platform || StrUtil.isBlank(platform.getRealmId())) {
            return null;
        }

        result = siPlatformQueryService.findHoldRelAssetPage(HoldAssetPageInfo.builder()
                .current(req.getCurrent())
                .pageSize(req.getPageSize())
                .holdId(req.getHoldId())
                .holdType(req.getHoldType())
                .assetType(req.getAssetType())
                .ownerId(req.getTargetId())
                .build());

        return userToAssetBriefPage(platform.getRealmId(), result, SiAssetRelation::getTargetId);
    }

    /**
     * 分页查询拥有指定资源的所有资源信息，比如查询拥有指定权限的所有分组信息
     */
    public SiPageResult<SiAssetBrief> queryHoldersOfAssetPage(HoldersOfAssetPageIn req) {
        if (!AuthConst.REALM_SERVER.equals(req.getServer())) {
            if ("USER".equals(req.getHoldType())) {
                return queryAssetsOfUserPage(req);
            } else {
                return siPlatformQueryService.findAssetHoldPage(AssetHoldPageInfo.builder()
                        .ownerId(req.getTargetId())
                        .current(req.getCurrent())
                        .pageSize(req.getPageSize())
                        .assetId(req.getAssetId())
                        .assetType(req.getAssetType())
                        .holdType(req.getHoldType())
                        .build());
            }
        }
        return null;
    }

    public SiPageResult<SiAssetBrief> queryAssetsOfUserPage(HoldersOfAssetPageIn req) {
        SiPageResult<SiAssetRelation> result;
        SiPlatform platform = siPlatformQueryService.lookupPlatform(SiPlatIdFind.builder()
                .platformId(req.getTargetId()).build());

        if (null == platform || StrUtil.isBlank(platform.getRealmId())) {
            return null;
        }

        result = siPlatformQueryService.findAssetRelHoldPage(AssetHoldPageInfo.builder()
                .current(req.getCurrent())
                .pageSize(req.getPageSize())
                .assetId(req.getAssetId())
                .assetType(req.getAssetType())
                .holdType(req.getHoldType())
                .ownerId(req.getTargetId())
                .build());

        return userToAssetBriefPage(platform.getRealmId(), result, SiAssetRelation::getAssetId);
    }

    public SiPageResult<SiAssetBrief> userToAssetBriefPage(String realmId, SiPageResult<SiAssetRelation> result,
                                                           Function<? super SiAssetRelation, ? extends String> mapper) {
        if (null == result || result.getData() == null || result.getData().isEmpty()) {
            return null;
        } else {
            List<SiUser> userList;
            userList = siUserQueryService.queryUsers(SiQueryUserIn.builder()
                    .realmId(realmId)
                    .userIds(result.getData().stream()
                            .map(mapper)
                            .collect(Collectors.toList()))
                    .build());
            if (null == userList) {
                return null;
            } else {
                SiPageResult<SiAssetBrief> briefPage = new SiPageResult<>();
                briefPage.setCurrent(result.getCurrent());
                briefPage.setPageSize(result.getPageSize());
                briefPage.setCount(userList.size());
                briefPage.setTotal(result.getTotal());
                briefPage.setData(userList.stream().map(item -> SiAssetBrief.builder()
                        .id(item.getId())
                        .name(StrUtil.isBlank(item.getNickName()) ? item.getName() : item.getNickName())
                        .code(item.getPhone())
                        .state(item.getState())
                        .props(SiProps.build(item.getProps()))
                        .build()).collect(Collectors.toList()));

                return briefPage;
            }
        }
    }

    /**
     * 读取资源属性
     */
    public Map<String, Object> loadAttributesOfAsset(AttributesOfAssetQueryIn req) {
        if ("REALM_USER".equals(req.getAssetType())) {
            return loadUserAttributes(req);
        } else if ("USER_GROUP".equals(req.getAssetType())) {
            return loadUserGroupAttributes(req);
        } else if ("platform".equalsIgnoreCase(req.getServer()) || "ASSET".equalsIgnoreCase(req.getServer())) {
            if ("PLATFORM".equals(req.getAssetType())) {
                return loadPlatformAttributes(req);
            } else {
                return loadAssetAttributes(req);
            }
        } else {
            if ("REALM".equals(req.getAssetType())) {
                return loadRealmAttributes(req);
            } else if ("USER".equals(req.getAssetType())) {
                return loadUserAttributes(req);
            } else if ("GROUP".equals(req.getAssetType())) {
                return loadUserGroupAttributes(req);
            } else {
                return new HashMap<>();
            }
        }
    }

    private Map<String, Object> loadRealmAttributes(AttributesOfAssetQueryIn req) {
        SiRealmDTO realm = siRealmQueryService.findRealm(req.getTargetId());
        if (null == realm) {
            throw CenterErrorCode.REALM_IS_DONT_EXIST.buildException();
        }

        return realm.getProps();
    }

    private Map<String, Object> loadUserAttributes(AttributesOfAssetQueryIn req) {
        SiUser siUser = siUserQueryService.lookupUserById(UserIdInfo.builder()
                .realmId(req.getTargetId())
                .userId(req.getAssetId())
                .build());
        if (null != siUser && null != siUser.getProps()) {
            siUser.getProps().removeProp("gender");
        }
        return null == siUser ? new HashMap<>() : siUser.getProps();
    }

    private Map<String, Object> loadUserGroupAttributes(AttributesOfAssetQueryIn req) {
        SiUserGroup siUser = siUserQueryService.lookupUserGroup(UserGroupLookupIn.builder()
                .realmId(req.getTargetId())
                .groupId(req.getAssetId())
                .build());
        if (null != siUser && null != siUser.getProps()) {
            siUser.getProps().removeProp("gender");
        }
        return null == siUser ? new HashMap<>() : siUser.getProps();
    }

    private Map<String, Object> loadPlatformAttributes(AttributesOfAssetQueryIn req) {
        SiPlatform plat = siPlatformQueryService.lookupPlatform(SiPlatIdFind.builder()
                .platformId(req.getTargetId()).build());
        if (null == plat) {
            throw CenterErrorCode.PLATFORM_IS_DONT_EXIST.buildException();
        }

        return plat.getProps();
    }

    private Map<String, Object> loadAssetAttributes(AttributesOfAssetQueryIn req) {
        SiAssetEntry entry = siPlatformQueryService.lookupAssetInfo(SiAssetId.builder()
                .ownerId(req.getTargetId())
                .assetId(req.getAssetId())
                .assetType(req.getAssetType())
                .build());
        if (null == entry || null == entry.getAsset()) {
            throw CenterErrorCode.ASSET_IS_DONT_EXIST.buildException();
        }

        return entry.getAsset().getProps();
    }
}
